最初听说 MongoDB 的时候,我总是觉得它的稳定性堪忧,后来用了差不多一年的时间,其实也没有遇到过什么问题,反而是 MySQL 出现过几次丢失数据的情况。配合 Node.js 使用 MongoDB 是一件非常舒畅的事情,从前端,到后端,再到数据库,统统全是 JSON.
本文的定位是一篇对 MongoDB 的一个概览性的介绍,告诉你 MongoDB 的特点和功能,以及如果需要了解某个功能,应当搜索什么关键词,并不直接涉及技术细节。
特点
-
无模式
MongoDB 中的每一条文档,都是一个 JSON 对象,因此你无需预定义一个集合的结构,集合中的每个文档也可以有不同的结构。
-
异步写入
MongoDB 默认所有的写操作都是『不安全』的,即当请求被 MongoDB 收到时,不等写入操作完成,就返回一个『成功』的响应。
这是默认的行为,当然你设置一些选项,让操作等待等待写入完成后再返回响应。不过对于大多数应用,这种『不安全』已经足够安全了。 -
简单查询
MongoDB 只支持简单的查询,MongoDB 只储存数据,更多的逻辑应该在应用中解决。因此 MongoDB 有着简单的且在各编程语言下高度一致的 API 接口。
-
无需运维
MongoDB 几乎没有什么选项可以设置,集群也是设置一次之后就可以自动地解决故障,极少需要维护。
安装和备份
MongoDB 的安装很简单,直接在官网下载二进制版本,运行其中的 mongod 即可启动数据库,运行 mongo 即可启动客户端 Shell, 或者你也可以从软件源中安装。
MongoDB 被设计运行于 64bit 的操作系统,在 32bit 的情况下,数据文件最大限制为 2GiB.
MongoDB 在运行时并不会实时地将修改写入磁盘,因此在关闭服务器时需要给 MongoDB 时间将所有数据写入磁盘。当出现服务器突然掉电的情况时,MongoDB 的数据库文件会损坏,需要进行修复才能重新运行,这个过程中会丢失掉电时正在进行的写入操作。在对运行中的 MongoDB 进行备份时,需要使用自带的 mongodump 工具,不能直接复制其数据库文件。
设计文档结构
-
ObjectID
MongoDB 会为每一个文档默认添加一个名为 _id, 类型为 Object 的字段,这个字段用来唯一地标识每一个文档。这个 ID 通过时间,服务器,进程号被生成,甚至可以认为是全世界唯一的。
除了 MongoDB 默认为 _id 添加 ObjectID, 你也可以自己在文档中创建 ObjectID, 来起到唯一地标识某个对象的功能。
引用关系
例如我们有一个 topic 集合,topic 都是 account 创建的,所以 topics 中要引用来自 accounts 中的 account.
// accounts
{
_id: <ObjectID 1>,
name: "jysperm"
}
// topics
{
_id: <ObjectID 2>,
account_id: <ObjectID 1>
}
嵌入关系
每个 topic 会有一些 reply, 所以在 topic 中可以嵌入 reply.
// topics
{
_id: <ObjectID 2>,
account_id: <ObjectID 1>
replies: [
{
_id: <ObjectID 3>,
content: "xxoo"
}
]
}
引用 Vs 嵌入
在上面第一个例子中,topic 其实也可以嵌入 account 中;而第二个例子中,reply 也可以使用一个新的集合,然后来引用 topic, 那么应该如何选择这两种关系呢。
我们主要从几个出发点来考虑:
-
查询
我们可以考虑查询 account 时,是否需要他所有的 topic, 或者查询 topic 时,是否需要它所有的 reply.
从查询的角度来考虑,如果需要一同获得这两种数据,那么就应该嵌入,如果不需要,就应该引用。 -
数据的增长性
我们还可以考虑 account 的 topic 数量在今后会有怎样的增长,topic 的 reply 数量会有怎样的增长。
如果增长是没有限度的,那么就应该引用,如果增长是有限的,那么就可以嵌入。 -
对应关系
如果是一对一关系,或者一对多关系,那么可以考虑嵌入,如果是多对多关系,那么应该引用。
-
原子性
MongoDB 仅在文档层面提供原子性,如果有两个非常敏感的数据需要同时被更新,那么他们有必要存在于同一个文档中。
查询和更新
所谓『增删查改』在 MongoDB 里对应:
- find/findOne: 查询
- update/save: 修改
- insert/save: 新增
- remove: 删除
在 MongoDB 中,操作符以 $
开头,主要分为三类:查询操作符,更新操作符,聚合查询操作符。
-
查询操作符
用于 find 和 update 中的查询器,如 $gt(大于), $ne(不等于), $in(匹配几个值之一), 逻辑运算:$and, $not.
-
更新操作符
用于 update 中的更新器,如 $inc(对数字进行增量), $set(修改文档的一部分), $unset(删除一个字段).
MongoDB 对嵌入式的文档和数组有非常好的支持:$addToSet(向集合中添加元素), $push(向数组添加元素), $pull(从数组移除元素). -
聚合查询(Aggregation)
类似于 SQL 数据库中的 GROUP, 提供统计和计算的功能,要多强大有多强大,毕竟可以直接在数据库中运行 JavaScript 代码,不过因为性能的关系,不适合在应用中频繁调用。
查询命令还有几个选项:
- limit: 限制返回的结果数
- skip: 跳过一些结果
- sort: 对结果进行排序
- fields: 只返回指定的字段
副本集(Replica Set)和分片(Sharding)
MongoDB 的副本集采用一主多从的的集群方式。副本集中只有主节点可以写入,其他节点从主节点同步。当主节点故障时,从节点中会自动推选出一个新的主节点。当故障的节点恢复后,会向其他节点同步到最新的数据,然后成为一个从节点。
MongoDB 的分片是指,按照某个字段的值,将数据分为多份,储存在不同的服务器上。客户端会运行一个 mongos 代替 mongod, mongos 相当于一个路由,会根据请求和分片的设置,将请求拆分后发给不同的服务器,得到结果后再将结果组合起来发给客户端。
分片可以与副本集同时使用,此时,每个分片都是一个副本级,这样可以提供非常高的可用率和扩展性。
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。